package io.gitee.zhucan123.core.infrastructure.dataconversion;


import com.alibaba.fastjson.parser.DefaultJSONParser;
import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer;
import com.alibaba.fastjson.serializer.JSONSerializer;
import com.alibaba.fastjson.serializer.ObjectSerializer;
import com.alibaba.fastjson.util.TypeUtils;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;

import javax.persistence.AttributeConverter;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * 本类 为code为数字类型的枚举类的转换基类 ,继承此类可以在jpa持久化时和实体类对象与数值字段 时 类型互相转换
 * 需要在 需要转换的enum 字段上面 添加@converter注解,并指定此转换类的class
 *
 * @author Administrator
 */
public abstract class AbstractByteEnumConverter<S extends IntegerEnum, M extends Number> extends JsonDeserializer<S> implements ObjectDeserializer, ObjectSerializer, AttributeConverter<S, M> {

  /**
   * 枚举类转为 code值
   *
   * @param attributeStatus
   * @return
   */
  @Override
  public M convertToDatabaseColumn(S attributeStatus) {
    //做空判断,因为数据库枚举类型传空时,这里会异常
    return attributeStatus == null ? null : (M) attributeStatus.getValue();
  }

  /**
   * code值 转为枚举类
   *
   * @param i
   * @return
   */
  @Override
  public S convertToEntityAttribute(M i) {
    if (i == null) {
      return null;
    }
    //获取 子类中 <S,T> 规定的参数类型
    Type[] actualTypeArguments = ((ParameterizedType) (getClass().getGenericSuperclass())).getActualTypeArguments();
    if (actualTypeArguments != null && actualTypeArguments.length > 0) {
      //获取第一个泛型参数 S
      Class<S> clazz = (Class<S>) actualTypeArguments[0];
      //判断是否是枚举类,获取枚举的所有实例
      if (Enum.class.isAssignableFrom(clazz)) {
        S[] values = clazz.getEnumConstants();
        for (S ele : values) {
          if (i.equals(ele.getValue())) {
            return ele;
          }
        }
      }

    }

    return null;
  }

  private S convertToEntityAttribute(String i) {
    if (i == null) {
      return null;
    }
    //获取 子类中 <S,T> 规定的参数类型
    Type[] actualTypeArguments = ((ParameterizedType) (getClass().getGenericSuperclass())).getActualTypeArguments();
    if (actualTypeArguments != null && actualTypeArguments.length > 0) {
      //获取第一个泛型参数 S
      Class<S> clazz = (Class<S>) actualTypeArguments[0];
      //判断是否是枚举类,获取枚举的所有实例
      if (Enum.class.isAssignableFrom(clazz)) {
        S[] values = clazz.getEnumConstants();
        for (S ele : values) {
          if (i.equals(ele.toString())) {
            return ele;
          }
        }
      }

    }

    return null;
  }

  /**
   * Get all enums
   *
   * @return
   */
  protected S[] values() {
    return null;
  }

  /**
   * 数字值 json反序列化为枚举对象 fastJson
   *
   * @param parser
   * @param type
   * @param fieldName
   * @param <T>
   * @return
   */
  @Override
  public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
    Object value = parser.parse();
    return value == null ? null : (T) convertToEntityAttribute(((M) TypeUtils.castToInt(value)));
  }

  @Override
  public int getFastMatchToken() {
    return 0;
  }

  /**
   * 把枚举对象的 序列化成字面量  fastJson
   *
   * @param serializer
   * @param object
   * @param fieldName
   * @param fieldType
   * @param features
   */
  @Override
  public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) {
    serializer.write(object.toString());
  }


  /**
   * JACKSON 的枚举反序列化
   *
   * @param p
   * @param ctxt
   * @param intoValue
   * @return
   */
  public S deserialize(JsonParser p, DeserializationContext ctxt, S intoValue) {
    return intoValue;
  }

  public S deserialize(JsonParser p, DeserializationContext ctxt)
      throws IOException, JsonProcessingException {
    JsonNode node = p.getCodec().readTree(p);
    try {
      return convertToEntityAttribute((M) Integer.valueOf(node.asText("0")));
    } catch (Exception e) {
      S deserialize = convertToEntityAttribute(node.asText());
      return deserialize;
    }

  }

  ;
}
